package org.ascollada.core
{
	import flash.errors.IllegalOperationError;
	import flash.utils.Dictionary;
	
	import org.ascollada.core.ns.collada;
	import org.ascollada.utils.DaeLogger;
	
	public class DaePrimitive extends DaeElement
	{
		use namespace collada;
		
		public var mesh :DaeMesh;
		public var count :int;
		public var type :String;
		public var material :String;
		public var vcount :Array;
		
		private var _sets :Object;
		private var _sources :Object;
		
		/**
		 * 
		 */ 
		public function DaePrimitive(document:DaeDocument, mesh:DaeMesh, element:XML=null)
		{
			this.mesh = mesh;
			
			super(document, element);
		}
		
		/**
		 * 
		 */ 
		public override function destroy():void
		{
			super.destroy();
			this.mesh = null;
		}
		
		/**
		 * 
		 */
		public function getDataSet(semantic:String, setID:uint):Array
		{
			if(_sets && _sets[semantic])
				return _sets[semantic][setID];
			return null;	
		} 
		
		/**
		 * 
		 */
		public function getDataSourceID(semantic:String, setID:uint):String
		{
			if(_sources && _sources[semantic])
				return _sources[semantic][setID];
			return null;	
		} 
		
		/**
		 * 
		 */
		public function getDefaultDataSetID(semantic:String):int
		{
			if(!_sets || !_sets[semantic]) return -1;
			
			var obj : Object = _sets[semantic];
			
			for(var id : String in obj)
			{
				if(obj[id] is Array)
					return parseInt(id, 10);	
			}
			
			return -1;
		}
		
		/**
		 * 
		 */ 
		public override function read(element:XML):void
		{
			super.read(element);
			
			var list :XMLList = element.input;
			var input :DaeInput;
			var inputs :Array = new Array();
			var verticesElement :XML = element.parent().vertices[0];
			var i :int;
			
			if(!verticesElement)
			{
				throw new IllegalOperationError("Could not find required <vertices> element!");
			}
			
			var verticesInput :DaeInput = new DaeInput(this.document, verticesElement.input.(@semantic=="POSITION")[0]);
			
			if(!verticesInput)
			{
				throw new IllegalOperationError("Could not find the required <input> element with @semantic='POSITION'!");
			}
			
			this.count = parseInt(readAttribute(element, "count"), 10);
			this.material = readAttribute(element, "material");
			this.type = element.name().localName;

			_sets = new Object();
			_sources = new Object();
			
			var offset :int = 0;
			
			for(i = 0; i < list.length(); i++)
			{
				input = new DaeInput(this.document, list[i]);
				
				// sets the @source of the input with @semantic = VERTEX to the
				// @source of the input with @semantic = POSITION
				input.source = (input.source == verticesElement.@id) ? verticesInput.source : input.source;
				
				var source :DaeSource = this.document.sources[input.source];
				
				if(!source)
				{
					throw new IllegalOperationError("Can't find a required <source> element!");	
				}
				
				inputs.push(input);
				
				_sets[ input.semantic ] = _sets[ input.semantic ] || new Object();
				_sets[ input.semantic ][input.setnum] = new Array();
				
				_sources[ input.semantic ] = _sources[ input.semantic ] || new Object();
				_sources[ input.semantic ][input.setnum] = input.source;
				
				offset = Math.max(offset, input.offset + 1);
			}
			
			if(element.vcount[0])
			{
				this.vcount = readStringArray(element.vcount[0]);
			}	
			
			switch(this.type)
			{
				case "lines":
				case "linestrips":
				case "polylist":
				case "polygons":
				case "trifans":
				case "tristrips":
				case "triangles":
					buildTriangles(element, inputs, offset);
					break;
				default:
					DaeLogger.log("[WARNING] primitives with type = '" + this.type + "' are not yet supported");
					break;
			}
		}

		/**
		 * 
		 */ 
		private function buildIndices(element:XML, offset:int):Array
		{
			var indices :Array;
			var i :int;
			if(this.type == "polygons")
			{
				this.vcount = new Array();
				
				indices = new Array();
				for(i = 0; i < element.p.length(); i++)
				{
					var tmp :Array = readStringArray(element.p[i]);
					
					this.vcount.push(tmp.length / offset);
					
					indices = indices.concat(tmp);
				}
			}	
			else if(element.p[0])
			{
				indices = readStringArray(element.p[0]);
			}
			return indices;
		}
		
		/**
		 * 
		 */ 
		private function buildTriangles(element:XML, inputs:Array, offset:int):void
		{
			var p: Array = buildIndices(element, offset);
			var input :DaeInput;
			var tmp :Dictionary = new Dictionary(true);
			var vc :int = 0;
			var vcount :Array = this.vcount;
			var num :int = (vcount && vcount.length) ? this.vcount[0] : (this.type == "lines" || this.type == "linestrips" ? 2 : 3);
			var i :int, j:int, index :int;
			
			for each(input in inputs)
			{
				tmp[input] = new Array();
			}
			
			i = 0;
			while(i < p.length)
			{
				for(j = 0; j < num; j++)
				{
					for each(input in inputs)
					{
						index = parseInt(p[i + input.offset], 10);
						tmp[input].push(index);
						
						if(tmp[input].length == num)
						{
							_sets[input.semantic][input.setnum].push(tmp[input]);
							
							var prev :int;
							switch(this.type)
							{
								case "trifans":
									prev = tmp[input].pop() as int;
									tmp[input].pop();
									tmp[input].push(prev);
									break;
								case "linestrips":
								case "tristrips":
									tmp[input].shift();
									break;
								default:
									tmp[input] = new Array();
									break;
							}
						}
					}
					i += offset;
				}
				num = (vcount && vcount.length) ? vcount[++vc] : 3;
			}
			
			if(this.type == "polylist" || this.type == "polygons")
			{
				triangulate(inputs);
			}
		}
		
		/**
		 * Simple triangulation
		 * 
		 * @param	inputs
		 */ 
		private function triangulate(inputs:Array):void
		{
			var input :DaeInput;
			var i :int, j :int;
			
			for each(input in inputs)
			{
				var data :Array = _sets[input.semantic][input.setnum];
				var triData :Array = new Array();
				
				for(i = 0; i < data.length; i++)
				{
					var indices :Array = data[i];
					
					for(j = 1; j < indices.length-1; j++)
					{
						var idx0 :int = indices[0];
						var idx1 :int = indices[j];
						var idx2 :int = indices[j+1];
						
						triData.push([idx0, idx1, idx2]);
					}
				}
				
				_sets[input.semantic][input.setnum] = triData;
			}
		}
	}
}